package com.ricky01;

import com.ricky01.entity.User;
import com.ricky01.service.UserService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.web.context.support.GenericWebApplicationContext;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

/**

 */
@EnableAutoConfiguration
public class SpringBootApplication05_01 {

    @Bean
    public User createUser(){
        return new User("ricky",10);
    }
    @Bean
    @Primary // 当使用类获取bean实例的时候使用
    public User createUser2(){
        return new User("ricky02",10);
    }

    @Bean("rickyUser") // 指定bean的名称
    public User createUser3(){
        return new User("ricky03",10);
    }

    @Bean
    public  BeanDefinitionRegistryPostProcessor createBeanDefinitionRegistryPost(){
        return new BeanDefinitionRegistryPostProcessor() {
            @Override
            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//                    BeanDefinitionBuilder builder=BeanDefinitionBuilder.rootBeanDefinition(User.class);
//                    registry.registerBeanDefinition("userBeanPost",builder.getBeanDefinition());

                System.out.println("----------#######postProcessBeanDefinitionRegistry------------------"+registry.getBeanDefinitionCount());
                RootBeanDefinition user=new RootBeanDefinition(User.class);
                registry.registerBeanDefinition("userBeanPost",user);
            }

            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                System.out.println("----------#######postProcessBeanFactory------------------"+beanFactory.getBeanDefinitionCount());
            }
        };
    }

    public static void main(String[] args) {
        // 第一步 创建SpringApplication对象
        SpringApplication application=new SpringApplication(SpringBootApplication05_01.class);
//        SpringApplication application2=new SpringApplication(SpringBootApplication05_01.class,UserService.class);
            // 第一处要说明的是构造传入的参数可以是多个 等价于下面的写法
            Set primarySources = new LinkedHashSet<>(Arrays.asList(SpringBootApplication05_01.class,UserService.class));
            application.addPrimarySources(primarySources);

            // 等价于设置active  即执行application-dev.yml文件
            application.setAdditionalProfiles("dev");

            // 可设置Banner模式 banner具体内容
            application.setBannerMode(Banner.Mode.CONSOLE);
            application.setBanner(new Banner() {
                @Override
                public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
                    out.println("////////////////////////////////////////////////////////////////////\n" +
                            "//                          _ooOoo_                               //\n" +
                            "//                         o8888888o                              //\n" +
                            "//                         88\" . \"88                              //\n" +
                            "//                         (| ^_^ |)                              //\n" +
                            "//                         O\\  =  /O                              //\n" +
                            "//                      ____/`---'\\____                           //\n" +
                            "//                    .'  \\\\|     |//  `.                         //\n" +
                            "//                   /  \\\\|||  :  |||//  \\                        //\n" +
                            "//                  /  _||||| -:- |||||-  \\                       //\n" +
                            "//                  |   | \\\\\\  -  /// |   |                       //\n" +
                            "//                  | \\_|  ''\\---/''  |   |                       //\n" +
                            "//                  \\  .-\\__  `-`  ___/-. /                       //\n" +
                            "//                ___`. .'  /--.--\\  `. . ___                     //\n" +
                            "//              .\"\" '<  `.___\\_<|>_/___.'  >'\"\".                  //\n" +
                            "//            | | :  `- \\`.;`\\ _ /`;.`/ - ` : | |                 //\n" +
                            "//            \\  \\ `-.   \\_ __\\ /__ _/   .-` /  /                 //\n" +
                            "//      ========`-.____`-.___\\_____/___.-`____.-'========         //\n" +
                            "//                           `=---='                              //\n" +
                            "//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //\n" +
                            "//         佛祖保佑       永无BUG     永不修改                    //\n" +
                            "//                                                    ricky制作   //\n" +
                            "////////////////////////////////////////////////////////////////////");
                }
            });
            // 关闭启动日志  默认true
            /**
             *
             true的时候会多出下面两行日志
             2018-05-17 10:24:50.585  INFO 20536 --- [           main] com.ricky01.SpringBootApplication05_01   : The following profiles are active: dev
             2018-05-17 10:24:50.697  INFO 20536 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@cd3fee8: startup date [Thu May 17 10:24:50 CST 2018]; root of context hierarchy

             */
            application.setLogStartupInfo(false);

            // 追加ConfigurableApplicationContext子类的 initialize
            //在ConfigurableApplicationContext刷新之前初始化Spring ConfigurableApplicationContext的回调接口
            // 其他可以通過application.yml中设置context.initializer.classes=xxxx或者在spring.factories中配置org.springframework.context.ApplicationContextInitializer=xxx
            //跟setInitializer类似
            application.addInitializers(new ApplicationContextInitializer<GenericApplicationContext>() {
                @Override
                public void initialize(GenericApplicationContext ctx) {
                    // 可以根据上下文提前设置一些参数
                    ctx.registerBean("rickyDefault",User.class,User::new); // 注入默认UserBean对象
                    ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
                    System.out.println(ctx.getBeanDefinitionCount());
                    ConfigurableEnvironment environment = ctx.getEnvironment();
                    System.out.println("ApplicationContextInitializer::::::::::"+environment.getPropertySources());

                }
            });

            // 可以监听 ApplicationEvent的子类的事件  比如context刷新和关闭等等
            // 在application.properties中配置context.listener.classes=xxx 或者
            /**
             *
             @Component
            public class XXClass{
                @EventListener
                public void onApplicationEvent(ContextRefreshedEvent event) {
                System.out.println("哎哟 等价 我监听到了呢");
                }
            }
             */

            // 跟setListeners类似

        ApplicationListener<ContextRefreshedEvent> applicationListener = new ApplicationListener<ContextRefreshedEvent>() {

            @Override
            public void onApplicationEvent(ContextRefreshedEvent event) {
                System.out.println("哎哟，我是这个时候刷新完了：" + event.getTimestamp() + ",刷新后你要干啥呢？");
            }
        };
        application.addListeners(applicationListener);



        //第二步执行 application的run方法  返回context对象
        ConfigurableApplicationContext context = application.run(args);
//        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) application.run(args);
//        AnnotationConfigServletWebServerApplicationContext context = (AnnotationConfigServletWebServerApplicationContext) application.run(args);

            // 获取所有的User的bean实例
            System.out.println(context.getBeansOfType(User.class));

            // 获取beanName是createUser的bean对象
            System.out.println("createUser-----------------"+context.getBean("createUser"));
            System.out.println("createUser2-----------------"+context.getBean("createUser2"));
            //当只有一个实例bean对象的时候 可以使用 User.class去获取   如果 确实想使用则 添加 @primary注解
            User defaultUser = context.getBean(User.class);
            System.out.println("User.class-----------------"+defaultUser);


            String[] BeanDefinitionNames = context.getBeanDefinitionNames();
            System.out.print("包含的bean对象：");
           for (String name:BeanDefinitionNames){
    //           System.out.println(name+",");
           }

            String applicationName = context.getApplicationName();
            System.out.println("applicationName:"+applicationName);
            //获取bean的数量
            int beannum=context.getBeanDefinitionCount();
            System.out.println("bean的数量:"+beannum);

            // 发布自定义事件
            context.publishEvent(new MyApplicationEvent(new Object()));
            // 等同上述 applicationListener
            context.addApplicationListener(applicationListener);

            // bean工厂加载完毕的后置通知  但是在这里无效  实现方式参照 createBeanDefinitionRegistryPost()方法
            context.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
                @Override
                public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                    System.out.println("--------------------------------"+beanFactory.getBeanDefinitionCount());
                }
            });
            //此方法是内部调用  这里不能再次调用 因为已经refresh过了
//            context.refresh();
        // 获取所有的User的bean实例
        System.out.println("----------------------后置注入以后  是否多了user_BeanPost对象------------------------------");
        System.out.println(context.getBeansOfType(User.class));
        UserService userService = context.getBean(UserService.class);
        userService.showUser();
        //关闭
        context.close();
    }

    /**
     * 自定义事件需要发布后方可在listener中使用
     */
   static class MyApplicationEvent extends ApplicationEvent{

        public MyApplicationEvent(Object source) {
            super(source);
        }
    }
}